import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

///
/// 自定义刷新(仿IOS)
///
/// 简述下自己对这个widget的理解
/// 1.其实就是一个Sliver的容器
/// 2.本身不支持手势，需要外部设置某些字段，update当前的widget
/// 3.这个示例仿照的demo，其本意是：设定一个固定的sliver，但在外部设置一个动画或手势可刷新这个Sliver布局
///
class _CupertinoSliverRefresh extends SingleChildRenderObjectWidget {
  const _CupertinoSliverRefresh({
    this.refreshIndicatorLayoutExtent = 0.0,
    this.hasLayoutExtent = false,
    Widget? super.child,
  }) : assert(refreshIndicatorLayoutExtent >= 0.0);

  final double refreshIndicatorLayoutExtent;

  final bool hasLayoutExtent;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _RenderCupertinoSliverRefresh(
        refreshIndicatorExtent: refreshIndicatorLayoutExtent,
        hasLayoutExtent: hasLayoutExtent);
  }

  @override
  void updateRenderObject(BuildContext context,
      covariant _RenderCupertinoSliverRefresh renderObject) {
    //重新渲染
    renderObject
      ..refreshIndicatorLayoutExtent = refreshIndicatorLayoutExtent
      ..hasLayoutExtent = hasLayoutExtent;
  }
}

///
/// 渲染器
///
/// RenderObjectWithChildMixin<RenderBox> 混入器, 当前RenderBox与唯一子组件RenderObject的关联，方便使用。
/// 简单来说就是在处理自身渲染的同时调用子组件的渲染api，作为有类似需求一个混入器方便使用避免重复代码的编写。
///
class _RenderCupertinoSliverRefresh extends RenderSliver
    with RenderObjectWithChildMixin<RenderBox> {
  double _refreshIndicatorExtent;
  bool _hasLayoutExtent;

  double get refreshIndicatorLayoutExtent => _refreshIndicatorExtent;

  set refreshIndicatorLayoutExtent(double value) {
    assert(value >= 0.0);
    if (value == _refreshIndicatorExtent) return;
    _refreshIndicatorExtent = value;

    //重新布局，RenderObject类中的方法
    markNeedsLayout();
  }

  bool get hasLayoutExtent => _hasLayoutExtent;

  set hasLayoutExtent(bool value) {
    if (value == _hasLayoutExtent) return;

    _hasLayoutExtent = value;
    markNeedsLayout();
  }

  //偏移量，每当extent有变化时，基于当前偏移量算出此次变化要移动多少
  double layoutExtentOffsetCompensation = 0.0;

  _RenderCupertinoSliverRefresh(
      {required double refreshIndicatorExtent,
      required bool hasLayoutExtent,
      RenderBox? child})
      : assert(refreshIndicatorExtent >= 0.0),
        _refreshIndicatorExtent = refreshIndicatorExtent,
        _hasLayoutExtent = hasLayoutExtent {
    this.child = child;
  }

  /// performLayout 设置布局位置
  @override
  void performLayout() {
    //断言：只在下拉时支持刷新
    assert(constraints.axisDirection == AxisDirection.down); //滚动轴方向
    assert(constraints.growthDirection == GrowthDirection.forward); //布局方向

    //下拉范围
    final double layoutExtent =
        (_hasLayoutExtent ? 1.0 : 0.0) * _refreshIndicatorExtent;
    if (layoutExtent != layoutExtentOffsetCompensation) {
      geometry = SliverGeometry(
          scrollOffsetCorrection:
              layoutExtent - layoutExtentOffsetCompensation);

      layoutExtentOffsetCompensation = layoutExtent;
      return;
    }

    //获取是否处于刷新状态，如果是需要动态计算出SliverGeometry
    //constraints.overlap是指：当前Sliver与之前的Sliver重叠程度。
    //举例：前一个Sliver实际绘制100px，当前的layoutExtent是50px，重叠区域就是50px，这个值为1/2
    final bool active = constraints.overlap < 0.0 || layoutExtent > 0.0;
    final double overScrolledExtent =
        constraints.overlap < 0.0 ? constraints.overlap.abs() : 0.0;

    child!.layout(
        constraints.asBoxConstraints(
            maxExtent: layoutExtent + overScrolledExtent),
        parentUsesSize: true);

    if (active) {
      geometry = SliverGeometry(
        scrollExtent: layoutExtent,
        paintOrigin: -overScrolledExtent - constraints.scrollOffset,
        //paintExtent 开始绘制时相对父布局的位置
        paintExtent: max(
            //取子部件与当前部件的最大值 ，减去已滚动距离，得出当前所在的位置,不能小于0
            max(child!.size.height, layoutExtent) - constraints.scrollOffset,
            0),
        maxPaintExtent: max(
            max(child!.size.height, layoutExtent) - constraints.scrollOffset,
            0),
        //剩余可滑动距离
        layoutExtent: max(layoutExtent - constraints.scrollOffset, 0.0),
      );
    } else {
      geometry = SliverGeometry.zero; //不占任何空间
    }
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    //表示有可绘制的内容
    if (constraints.overlap < 0.0 ||
        constraints.scrollOffset + child!.size.height > 0) {
      context.paintChild(child!, offset);
    }
  }
}

enum RefreshIndicatorState {
  inactive,
  drag,
  armed, //当拖动很远，onRefresh时执行
  refresh,
  done,
}

/// 刷新时展示的Widget
typedef RefreshControlIndicatorBuilder = Widget Function(
  BuildContext context,
  RefreshIndicatorState? refreshState,
  double pulledExtent,
  double refreshTriggerPullDistance,
  double refreshIndicatorExtent,
);

///刷新回调
typedef RefreshCallback = Future<void> Function();

class CupertinoSliverRefreshControl extends StatefulWidget {
  static const double _defaultRefreshTriggerPullDistance = 100.0;
  static const double _defaultRefreshIndicatorExtent = 60.0;

  static Widget buildSimpleRefreshIndicator(
    BuildContext? context,
      RefreshIndicatorState? refreshState,
    double? pulledExtent,
    double? refreshTriggerPullDistance,
    double? refreshIndicatorExtent,
  ) {
    const Curve opacityCurve = Interval(0.4, 0.8, curve: Curves.easeInOut);
    return Align(
      alignment: Alignment.bottomCenter,
      child: Container(
        width: double.infinity,
        height: 200,
        color: Colors.grey,

      ),
    );
  }

  final double refreshTriggerPullDistance;
  final double refreshIndicatorExtent;
  final RefreshControlIndicatorBuilder? builder;
  final RefreshCallback? onRefreshCallback;

  CupertinoSliverRefreshControl({
    super.key,
    this.refreshTriggerPullDistance = _defaultRefreshTriggerPullDistance,
    this.refreshIndicatorExtent = _defaultRefreshIndicatorExtent,
    this.builder = buildSimpleRefreshIndicator,
    this.onRefreshCallback,
  })  : assert(refreshTriggerPullDistance > 0.0),
        assert(refreshIndicatorExtent >= 0.0),
        assert(
            refreshTriggerPullDistance >= refreshIndicatorExtent,
            'The refresh indicator cannot take more space in its final state '
            'than the amount initially created by overscrolling.');

  @override
  State<StatefulWidget> createState() => CupertinoSliverRefreshControlState();
}

class CupertinoSliverRefreshControlState
    extends State<CupertinoSliverRefreshControl> {
  static const double _inactiveResetOverscrollFraction = 0.1;
  RefreshIndicatorState? refreshState; //刷新状态
  Future<void>? refreshTask;

  double latestIndicatorBoxExtent = 0.0;
  bool hasSliverLayoutExtent = false;
  bool needRefresh = false;
  bool draging = false;

  @override
  void initState() {
    super.initState();
    refreshState = RefreshIndicatorState.inactive;
  }

  ///切换状态
  RefreshIndicatorState? transitionNextState() {
    RefreshIndicatorState? nextState;
    void goToDone() {
      nextState = RefreshIndicatorState.done;
      //SchedulerBinding可以判断帧绘制状态
      if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
        setState(() => hasSliverLayoutExtent = false);
      } else {
        //非空闲状态，通过回调再执行
        SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
          setState(() => hasSliverLayoutExtent = false);
        });
      }
    }

    switch (refreshState) {
      case null:
      case RefreshIndicatorState.inactive:
        if (latestIndicatorBoxExtent <= 0) {
          return RefreshIndicatorState.inactive;
        } else if (latestIndicatorBoxExtent <
            widget.refreshTriggerPullDistance) {
          return RefreshIndicatorState.drag;
        }
        continue drag;
      case RefreshIndicatorState.armed:
        if (refreshState == RefreshIndicatorState.armed && !needRefresh) {
          goToDone();
          continue done;
        }

        //当前距离如果大于刷新距离，那么认为他仍然是armed状态，直到==refreshIndicatorExtent
        if (latestIndicatorBoxExtent > widget.refreshIndicatorExtent) {
          return RefreshIndicatorState.armed;
        } else {
          //仍然在拖拽
          if (draging) {
            goToDone();
            continue done;
          }

          nextState = RefreshIndicatorState.refresh;
        }
        continue refresh;
      refresh:
      case RefreshIndicatorState.refresh:
        //刷新状态
        if (needRefresh) {
          if (widget.onRefreshCallback != null && refreshTask == null) {
            HapticFeedback.mediumImpact();
            SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
              refreshTask = widget.onRefreshCallback!()
                ..whenComplete(() {
                  //对照箭头函数，可以访问所在对象中的变量
                  if (mounted) {
                    setState(() {
                      refreshTask = null;
                      needRefresh = false;
                    });
                    refreshState = transitionNextState();
                  }
                });
              setState(() => hasSliverLayoutExtent = true);
            });
          }

          return RefreshIndicatorState.refresh;
        } else {
          goToDone();
        }
        continue done;

      done:
      case RefreshIndicatorState.done:
        //结束
        if (latestIndicatorBoxExtent >
            widget.refreshTriggerPullDistance *
                _inactiveResetOverscrollFraction) {
          return RefreshIndicatorState.done;
        } else {
          nextState = RefreshIndicatorState.inactive;
        }
        break;
      drag:
      case RefreshIndicatorState.drag:
        if (latestIndicatorBoxExtent == 0) {
          return RefreshIndicatorState.inactive;
        } else if (latestIndicatorBoxExtent <
            widget.refreshTriggerPullDistance) {
          return RefreshIndicatorState.drag;
        } else {
          ///超过 refreshTriggerPullDistance 就可以进入准备刷新的装备状态
          if (widget.onRefreshCallback != null) {
            HapticFeedback.mediumImpact();
            SchedulerBinding.instance
                .addPostFrameCallback((Duration timestamp) {
              needRefresh = true;
              setState(() => hasSliverLayoutExtent = true);
            });
          }
          return RefreshIndicatorState.armed;
        }
    }

    return nextState;
  }

  /// 获取外部滚动事件
  void notifyScrollNotification(ScrollNotification notification){
    //手离屏
    if(notification is ScrollEndNotification){
      if(refreshState==RefreshIndicatorState.armed){
        draging=false;
      }
    }else if(notification is UserScrollNotification){//在拖动
      //ScrollDirection.idle 是指没有滚动了
      if(notification.direction!=ScrollDirection.idle){
        draging=true;
      }else{
        draging=false;
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return _CupertinoSliverRefresh(
      refreshIndicatorLayoutExtent: widget.refreshIndicatorExtent,
      hasLayoutExtent: hasSliverLayoutExtent,// false 不展示界面
      child: LayoutBuilder(
        //绘制刷新界面
        builder: (BuildContext context,BoxConstraints constraints){
          //这个决定图的变化
          latestIndicatorBoxExtent = constraints.maxHeight;// 问题出现在了 BoxConstraints maxHeight==0，原因是hasLayoutExtent为false
          //这个状态变化，会重新绘制自定义的Sliver
          refreshState=transitionNextState();
          if(widget.builder!=null && latestIndicatorBoxExtent>0){
            var result = widget.builder?.call(
              context,
              refreshState,
              latestIndicatorBoxExtent,
              widget.refreshTriggerPullDistance,
              widget.refreshIndicatorExtent
            );
            return result!;
          }
          return Container();
        },
      ),
    );
  }
}
